/*
    Copyright 2006-2011 Patrik Jonsson, sunrise@familjenjonsson.org

    This file is part of Sunrise.

    Sunrise is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Sunrise is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Sunrise.  If not, see <http://www.gnu.org/licenses/>.

*/

/** \file

    Declarations of threading utility functions. */

#ifndef __threadlocal__
#define __threadlocal__

#include "blitz/array.h"
#include "mcrx-debug.h"
#include <iostream>
#include <tbb/atomic.h>

namespace mcrx {

  /** Sets the array to the specified thread local policy, displaying an
      error message if it fails and debugging is enabled.  */
  template<typename T, int N>
  bool threadLocal_warn(blitz::Array<T,N>& a, bool local=true) 
  {
    bool success;
    if(!(success=a.threadLocal(local))) {
      DEBUG(1,std::cerr << "Warning: Could not set array locking!\n";);
    }
    return success;
  }

  /** Increments an atomic counter, if doing so will not make it
      no greater than max. Returns true if it incremented, false if
      not. */
  template<typename T_int>
  bool increment_if_less_than(tbb::atomic<T_int>& ctr, T_int max)
  {
    T_int oldval, newval;
    do {
      oldval = ctr;
      newval = oldval+1;
      if( newval > max) 
	// increment would take us above max, abort
	break;
    } while (ctr.compare_and_swap(newval, oldval) != oldval);
    
    return newval <= max;
  }

  /** Decrements an atomic counter if it was greater than
      zero. Returns true if it decremented, false if not. */
  template<typename T_int>
  bool decrement_if_greater_than_zero(tbb::atomic<T_int>& ctr)
  {
    T_int oldval, newval;
    do {
      oldval = ctr;
      newval = oldval-1;
      if( oldval == 0) 
	// old value is zero, don't decrement
	break;
    } while (ctr.compare_and_swap(newval, oldval) != oldval);
    
    return oldval > 0;
  }

  /** Binds the thread to a cpu, returning true if successful. The cpu
      number is calculated based on the thread_number and the
      bank_size, which can be used to avoid binding threads to the
      same physical cpu for processors with hyperthreading. */
  bool bind_thread(int, int);

  void numa_info();

  /** Binds NUMA memory allocations to the local cpu, returning
      true if successful. */
  bool bind_allocations();

  /** Sets the NUMA memory allocation policy to interleave, which
      spreads the pages out across all nodes. This should be
      advantageous for shared data like the image arrays. */
  bool interleave_allocations();
}

#endif
